#ifndef __CODE_ARM7_H__
#define __CODE_ARM7_H__

enum Arm7Register
{
    _r0=0,  _r1,    _r2,    _r3,    _r4,    _r5,    _r6,    _r7,
    _r8,    _r9,    _r10,   _fp,    _ip,    _sp,    _lr,    _pc
};


#define OPBITS( a, b, c, d ) ( ( a << 3 ) | ( b << 2 ) | ( c << 1 ) | ( d ) )

#define SBZ        0x0000                    // SBZ = Should be zero
#define ignore     0xFFFFFFFF

#define MASK_CONDITION        0xF0000000
enum Arm7Conditions
{
    Arm7Equal               =    OPBITS( 0, 0, 0, 0 ),        // Equal
    Arm7NotEqual            =    OPBITS( 0, 0, 0, 1 ),        // Not equal
    Arm7GreaterEqualU       =    OPBITS( 0, 0, 1, 0 ),        // Carry Set/unsigned higher or same
    Arm7LessU               =    OPBITS( 0, 0, 1, 1 ),        // Carry Clear/unsigned lower
    
    Arm7Negative            =    OPBITS( 0, 1, 0, 0 ),        // Minus/Negative
    Arm7PositiveZero        =    OPBITS( 0, 1, 0, 1 ),        // Plus/Positive or zero
    Arm7Overflow            =    OPBITS( 0, 1, 1, 0 ),        // Overflow
    Arm7NoOverflow          =    OPBITS( 0, 1, 1, 1 ),        // No overflow
    
    Arm7GreaterU            =    OPBITS( 1, 0, 0, 0 ),        // Unsigned higher
    Arm7LessEqualU          =    OPBITS( 1, 0, 0, 1 ),        // Unsigned lower or same
    Arm7GreaterEqual        =    OPBITS( 1, 0, 1, 0 ),        // Signed greater than or equeal
    Arm7Less                =    OPBITS( 1, 0, 1, 1 ),        // Signed less than

    Arm7Greater             =    OPBITS( 1, 1, 0, 0 ),        // Signed greater than
    Arm7LessEqual           =    OPBITS( 1, 1, 0, 1 ),        // Signed less than or equal
    Arm7Always              =    OPBITS( 1, 1, 1, 0 ),        // Always
    Arm7Never               =    OPBITS( 1, 1, 1, 1 )         // Never - Dont Use!
};


typedef struct _cilopnames
{
    int opcode;
    char text[32];
} CILOpNames;

// -------------------------------------------------------------------------------------

// Branch and Branch with Link instruction
#define MASK_BRANCH            0x0E000000
#define CODE_BRANCH            0x0A000000
#define IS_BRANCH( x ) ( ( x & ( MASK_BRANCH ) ) == CODE_BRANCH )

typedef struct _branchbits    // ARM
{ 
    uint32
    offset  :  24,
    link    :   1,    
    FIX0    :   3,    
    cond    :   4;
} BranchBits;

typedef union _branchcode
{
    uint32 code;
    BranchBits bits;
} BranchCode;

// Data processing instructions
#define MASK_DATA_IMM        0x0E000000
#define CODE_DATA_IMM        0x02000000
#define IS_DATA_IMM( x ) ( ( x & ( MASK_DATA_IMM ) ) == CODE_DATA_IMM )

typedef struct _dataimmbits
{ 
    uint32
    imm         :   8,
    rot         :   4,
    rd          :   4,
    rn          :   4,
    setCond     :    1,
    op          :   4,    
    FIX0        :   3,    
    cond        :   4;
} DataImmBits;

typedef union _dataimmcode
{
    uint32 code;
    DataImmBits bits;
} DataImmCode;
// -----

#define MASK_DATASHIFT_IMM        0x0E000010
#define CODE_DATASHIFT_IMM        0x00000000
#define IS_DATASHIFT_IMM( x ) ( ( x & ( MASK_DATASHIFT_IMM ) ) == CODE_DATASHIFT_IMM )

typedef struct _datashiftimmbits
{ 
    uint32
    rm          :   4,
    FIX1        :   1,
    shifttyp    :   2,
    shift       :   5,
    rd          :   4,
    rn          :   4,
    setCond     :    1,
    op          :   4,    
    FIX0        :   3,    
    cond        :   4;
} DataShiftImmBits;

typedef union _datashiftimmcode
{
    uint32 code;
    DataShiftImmBits bits;
} DataShiftImmCode;
// -----

#define MASK_DATASHIFT_REG        0x0E000090
#define CODE_DATASHIFT_REG        0x00000010
#define IS_DATASHIFT_REG( x ) ( ( x & ( MASK_DATASHIFT_REG ) ) == CODE_DATASHIFT_REG )

typedef struct _datashiftregbits
{ 
    uint32
    rm          :   4,
    FIX1        :   1,
    shifttyp    :    2,
    FIX         :   1,
    rs          :   4,
    rd          :   4,
    rn          :   4,
    setCond     :    1,
    op          :   4,    
    FIX0        :   3,    
    cond        :   4;
} DataShiftRegBits;

typedef union _datashiftregcode
{
    uint32 code;
    DataShiftRegBits bits;
} DataShiftRegCode;

enum Arm7DataOp
{
    Arm7And    = OPBITS( 0, 0, 0, 0 ),
    Arm7Eor    = OPBITS( 0, 0, 0, 1 ),
    Arm7Sub    = OPBITS( 0, 0, 1, 0 ),
    Arm7Rsb    = OPBITS( 0, 0, 1, 1 ),
    Arm7Add    = OPBITS( 0, 1, 0, 0 ),
    Arm7Adc    = OPBITS( 0, 1, 0, 1 ),
    Arm7Sbc    = OPBITS( 0, 1, 1, 0 ),
    Arm7Rsc    = OPBITS( 0, 1, 1, 1 ),
    Arm7Tst    = OPBITS( 1, 0, 0, 0 ),
    Arm7Teq    = OPBITS( 1, 0, 0, 1 ),
    Arm7Cmp    = OPBITS( 1, 0, 1, 0 ),
    Arm7Cmn    = OPBITS( 1, 0, 1, 1 ),
    Arm7Orr    = OPBITS( 1, 1, 0, 0 ),
    Arm7Mov    = OPBITS( 1, 1, 0, 1 ),
    Arm7Bic    = OPBITS( 1, 1, 1, 0 ),
    Arm7Mvn    = OPBITS( 1, 1, 1, 1 )
};

enum Arm7ShiftType
{
    Arm7Lsl,
    Arm7Lsr,
    Arm7Asr,
    Arm7Ror,
    Arm7Asl,
    Arm7Rrx
};

// Multiply Instructions
#define MASK_MULTIPLY        0x0F0000F0
#define CODE_MULTIPLY        0x00000090
#define IS_MULTIPLY( x ) ( ( x & ( MASK_MULTIPLY ) ) == CODE_MULTIPLY )

typedef struct _multiplybits
{ 
    uint32
    rm          :   4,
    FIX1        :   4,
    rs          :   4,
    rn          :   4,
    rd          :   4,
    setCond     :   1,
    op          :   3,    
    FIX0        :   4,    
    cond        :   4;
} MultiplyBits;

typedef union _multiplycode
{
    uint32 code;
    MultiplyBits bits;
} MultiplyCode;

enum Arm7MultiplyOp
{
    Arm7Mul         = OPBITS( 0, 0, 0, 0 ),
    Arm7Mla         = OPBITS( 0, 0, 0, 1 ),
    Arm7Umull       = OPBITS( 0, 1, 0, 0 ),
    Arm7Umlal       = OPBITS( 0, 1, 0, 1 ),
    Arm7SMull       = OPBITS( 0, 1, 1, 0 ),
    Arm7Smlal       = OPBITS( 0, 1, 1, 1 )
};

// Single word and unsigned byte data transfer instructions
#define MASK_WTRANS_IMM        0x0E000000
#define CODE_WTRANS_IMM        0x04000000
#define IS_WTRANS_IMM( x ) ( ( x & ( MASK_WTRANS_IMM ) ) == CODE_WTRANS_IMM )

typedef struct _wtransimmbits
{ 
    uint32
    imm         :    12,
    rd          :   4,
    rn          :   4,
    ls          :   1,                // load/store
    wback       :   1,                // write back (auto-index)
    bw          :   1,                // byte/word
    updown      :    1,                // up/down
    pindex      :   1,                // pre-/post-index
    FIX0        :   3,    
    cond        :   4;
} WTransImmBits;

typedef union _wtransimmcode
{
    uint32 code;
    WTransImmBits bits;
} WTransImmCode;
// -----

#define MASK_WTRANS_REG        0x0E000010
#define CODE_WTRANS_REG        0x06000000
#define IS_WTRANS_REG( x ) ( ( x & ( MASK_WTRANS_REG ) ) == CODE_WTRANS_REG )

typedef struct _wtransregbits
{ 
    uint32
    rm          :   4,
    FIX1        :   1,
    shifttyp    :   2,
    shift       :   5,
    rd          :   4,
    rn          :   4,
    ls          :   1,                // load or store
    wback       :   1,                // write back (auto-index) the new address
    bw          :   1,                // byte or word
    updown      :   1,                // up/down addressing 1 = add, 0 = sub
    pindex      :   1,                // pre-/post-index, pre = 1, post = 0, pre was marked with ! in the assembler source
    FIX0        :   3,    
    cond        :   4;
} WTransRegBits;

typedef union _wtransregcode
{
    uint32 code;
    WTransRegBits bits;
} WTransRegCode;

enum Arm7AdressingForLS
{
    Arm7PostIndex = 0,
    Arm7PreIndex = 1
};

enum Arm7UpDownAdressing
{
    Arm7SubtractAdressing = 0,
    Arm7AdditionAdressing = 1
};

enum Arm7BitWidth
{
    Arm7WordTransfer = 0,
    Arm7ByteTransfer = 1
};

enum Arm7AdressWriteBack
{
    Arm7NoWriteBack = 0,
    Arm7WriteBack = 1
};

enum Arm7TransferFunc
{
    Arm7Store = 0,
    Arm7Load = 1
};

// Half word and signed byte data transfer instructions
#define MASK_HWTRANS_IMM    0x0E400090
#define CODE_HWTRANS_IMM    0x00400090
#define IS_HWTRANS_IMM( x ) ( ( x & ( MASK_HWTRANS_IMM ) ) == CODE_HWTRANS_IMM )

typedef struct _hwtransimmbits
{ 
    uint32
    immLo       :   4,
    FIX3        :   1,
    type        :   2,                // s, h = type of what we want to store look at enum Arm7SignedBitWidth
    FIX2        :   1,
    immHi       :   4,
    rd          :   4,
    rn          :   4,
    ls          :   1,                // load/store
    wback       :   1,                // write back (auto-index)
    FIX1        :   1,
    updown      :   1,                // up/down
    pindex      :   1,                // pre-/post-index
    FIX0        :   3,    
    cond        :   4;
} HWTransImmBits;

typedef union _hwtransimmcode
{
    uint32 code;
    HWTransImmBits bits;
} HWTransImmCode;
// -----

// Half-word and signed byte data transfer instructions
#define MASK_HWTRANS_REG    0x0E400090
#define CODE_HWTRANS_REG    0x00000090
#define IS_HWTRANS_REG( x ) ( ( ( x & ( MASK_HWTRANS_REG ) ) == CODE_HWTRANS_REG ) && ( ( x & ( 0x60 ) ) != 0x00 ) ) // Bits 6 or 5 must be set to 1. if not it is a multiply instruction

typedef struct _hwtransregbits
{ 
    uint32
    rm      :   4,
    FIX3    :   1,
    type    :   2,
    FIX2    :   5,
    rd      :   4,
    rn      :   4,
    ls      :   1,                // load/store
    wback   :   1,                // write back (auto-index)
    FIX1    :   1,
    updown  :   1,                // up/down
    pindex  :   1,                // pre-/post-index
    FIX0    :   3,    
    cond    :   4;
} HWTransRegBits;

typedef union _hwtransregcode
{
    uint32 code;
    HWTransRegBits bits;
} HWTransRegCode;

enum Arm7SignedBitWidth
{
    Arm7TransferSignedByte              = 2,
    Arm7TransferUnsignedHalfWord        = 1,
    Arm7TransferSignedHalfWord          = 3
};

inline bool is24BitDistance(int dist)
{
    bool result = false;
    if (dist >= -8388608 && dist <= 8388607)
    {
        return true;
    }
    return result;
}

#endif //#ifndef __CODE_ARM7_H__
